package com.xyy.saas.payment.service.impl;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.pingan.openbank.api.sdk.client.ApiClient;
import com.pingan.openbank.api.sdk.entity.FileRequest;
import com.xyy.framework.redis.core.RedisClient;
import com.xyy.saas.payment.common.constants.ChannelConstants;
import com.xyy.saas.payment.constants.Constants;
import com.xyy.saas.payment.cores.api.PaymentMerchantApi;
import com.xyy.saas.payment.cores.bo.UserBo;
import com.xyy.saas.payment.cores.enums.AccountChannelEnum;
import com.xyy.saas.payment.cores.enums.BalanceAccountTypeEnum;
import com.xyy.saas.payment.cores.param.AccountTransferParam;
import com.xyy.saas.payment.cores.vo.CustAcctInfoVo;
import com.xyy.saas.payment.cores.vo.QueryAccountVo;
import com.xyy.saas.payment.cores.vo.ResultVO;
import com.xyy.saas.payment.cores.vo.SettleOrderVo2;
import com.xyy.saas.payment.dao.mapper.RefundOrderMapper;
import com.xyy.saas.payment.dao.model.*;
import com.xyy.saas.payment.dto.PinganLoanCheckAmountDto;
import com.xyy.saas.payment.exception.PaymentException;
import com.xyy.saas.payment.manage.cores.enums.ResultCodeEnum;
import com.xyy.saas.payment.manage.cores.vo.ResultPageVo;
import com.xyy.saas.payment.pojo.dto.ProfitSharingSettleDTO;
import com.xyy.saas.payment.pojo.vo.PayFinanceRefundPlanVo;
import com.xyy.saas.payment.pojo.vo.PinganRepaymentPlanParamVO;
import com.xyy.saas.payment.pojo.vo.UserAccountWithdrawalDTO;
import com.xyy.saas.payment.rpc.EcOrderRemote;
import com.xyy.saas.payment.service.*;
import com.xyy.saas.payment.trace.AlarmService;
import com.xyy.saas.payment.trace.DingTalkMessage;
import com.xyy.saas.payment.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
@DependsOn("apolloUtil")
public class PaymentLoanServiceImpl implements PinganLoanService {

    private static ApiClient apiClient = ApiClient.getInstance(ApolloUtil.getProperty("pingan.configPath"));

    @Autowired
    private PinganLoanFinanceRefundPlanService pinganLoanFinanceRefundPlanService;
    @Autowired
    private PayResultOrderService payResultOrderService;
    @Autowired
    private EcOrderRemote ecOrderRemote;
    @Autowired
    protected ApplicationContext applicationContext;
    @Autowired
    private RedisClient redisClient;
    @Autowired
    private RedisTemplate redisTemplate;
    @Resource
    private RefundOrderMapper refundOrderMapper;
    @Autowired
    private AlarmService alarmService;
    @Autowired
    private PayAccountService payAccountService;
    @Autowired
    private ThirdUserInfoService thirdUserInfoService;
    @Reference(version = "1.0.0")
    private PaymentMerchantApi paymentMerchantApi;
    @Autowired
    private LeafUtil leafUtil;

    @Override
    public void transferToPlatformCommissionAccount(UserAccountWithdrawalDTO withdrawalDTO) throws PaymentException {
        log.info("PaymentLoanServiceImpl transferToPlatformCommissionAccount param:{}", JSON.toJSONString(withdrawalDTO));

        withdrawalDTO.checkParam();

        String lockKey = "transferToPlatformCommissionAccount_" + withdrawalDTO.getUserId();
        if (!Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, "default_value"))) {
            throw new PaymentException(ResultCodeEnum.NOT_CONTINUOUS_CLICK);
        }
        redisTemplate.expire(lockKey, 10, TimeUnit.SECONDS);

        // 校验金额
        String withdrawableBalance = this.queryPinganWithdrawableBalance(withdrawalDTO.getUserId());
        if (StringUtils.isBlank(withdrawableBalance) || new BigDecimal(withdrawableBalance).compareTo(new BigDecimal(withdrawalDTO.getAmount())) != 0) {
            log.error("PaymentLoanServiceImpl transferToPlatformCommissionAccount userId:{}, error:{}",
                    withdrawalDTO.getUserId(),
                    ResultCodeEnum.WITHDRAWAL_DISALLOWED.getDisplayMsg());
            throw new PaymentException(ResultCodeEnum.WITHDRAWAL_DISALLOWED);
        }

        PayAccount payAccount = payAccountService.selectByBusiness("ec_pop", withdrawalDTO.getUserId());
        if (payAccount == null) {
            throw new PaymentException(ResultCodeEnum.USER_INFO_NOT_FOUND);
        }

        AccountTransferParam accountTransferParam = AccountTransferParam.builder()
                .transferType(AccountTransferParam.TRANSFER_REFUND) // fixme  TRANSFER_PAY or TRANSFER_REFUND ?
                .businessIdType("ec_pop")
                .outAccountId(payAccount.getAccountId())
                .inAccountId(Constants.specificOutAccountId)
                .amount(AmountCastUtil.castYuan2Fen(withdrawableBalance))
                .businessOrderNo(generateTransferToPlatformCommissionAccountNo(withdrawalDTO.getUserId()))  // fixme 生成规则
                .remark("商家平安可提现金额提现至平台佣金户")
                .build();
        ResultVO<Boolean> resultVO = paymentMerchantApi.accountTransfer(accountTransferParam);
        log.info("PaymentLoanServiceImpl transferToPlatformCommissionAccount param:{}, result:{}", JSON.toJSONString(withdrawalDTO), JSON.toJSONString(resultVO));

        if (resultVO == null || resultVO.isFail() || resultVO.getResult() == null) {
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }

    }

    private String generateTransferToPlatformCommissionAccountNo(String userId) {
        return "TransferP" + userId + leafUtil.getSnowflakeId();
    }

    @Override
    public void settle(ProfitSharingSettleDTO settleDTO) throws PaymentException {
        String reqParam = JSON.toJSONString(settleDTO);
        log.info("PaymentLoanServiceImpl settle parma:{}", reqParam);

        settleDTO.checkParam();

        SettleOrderVo2 settleOrderVo2 = new SettleOrderVo2();
        // todo

        ResultVO<Boolean> resultVO = paymentMerchantApi.settle(settleOrderVo2);
        log.info("PaymentLoanServiceImpl settle result:{}", JSON.toJSONString(resultVO));
        if (resultVO == null || resultVO.isFail() || resultVO.getResult() == null) {
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    @Override
    public String queryPinganFrozenAmount(String userId) throws PaymentException {
        log.info("queryPinganFrozenAmount userId:{}", userId);
        if (StringUtils.isBlank(userId)) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
        }
        String accountId = payAccountService.getAccountIdByBusiness("ec_pop", userId);
        if (StringUtils.isBlank(accountId)) {
            throw new PaymentException(ResultCodeEnum.USER_INFO_NOT_FOUND);
        }
        ResultVO<CustAcctInfoVo> resultVO = paymentMerchantApi.queryCustAcctId(accountId);

        log.info("queryPinganFrozenAmount userId:{} resultVO={}", userId, JSONUtils.toJSON(resultVO));
        if (resultVO == null || resultVO.isFail() || resultVO.getResult() == null) {
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }
        CustAcctInfoVo custAcctInfoVo = resultVO.getResult();
        return AmountCastUtil.decimalFormat(custAcctInfoVo.getSubAcctFreezeAmt());
    }

    @Override
    public String queryPinganWithdrawableBalance(String orgId) throws PaymentException {
        log.info("withdrawableBalance orgId:{}", orgId);
        if (StringUtils.isBlank(orgId)) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
        }
        PayAccount payAccount = payAccountService.selectByBusiness("ec_pop", orgId);
        //PayAccount payAccount = payAccountService.getPayAccountByAccountId(userId);
        if (payAccount == null) {
            throw new PaymentException(ResultCodeEnum.USER_INFO_NOT_FOUND);
        }
        String userId = payAccount.getAccountId();
        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(userId);
        if (thirdUserInfo == null || StringUtils.isBlank(thirdUserInfo.getThirdUserId())) {
            throw new PaymentException(ResultCodeEnum.THIRD_USER_ID_NOT_EXIST);
        }
        String thirdType = thirdUserInfo.getThirdType();
        if (!ChannelConstants.CHANNEL_PINGAN_NAME.equals(thirdUserInfo.getThirdType())) {
            log.error("withdrawableBalance thirdType is not supported. userId:{}, thirdType:{}", userId, thirdType);
            throw new PaymentException(ResultCodeEnum.UN_SUPPORT_METHOD);
        }
        QueryAccountVo queryAccountVo = new QueryAccountVo();
        queryAccountVo.setBusinessId(payAccount.getBusinessUserId());
        queryAccountVo.setBusinessIdType(payAccount.getBusinessIdType());
        queryAccountVo.setInvoker(payAccount.getBusinessIdType());
        queryAccountVo.setAccountChannelEnum(AccountChannelEnum.AGGREGATE);
        queryAccountVo.setBalanceAccountType(BalanceAccountTypeEnum.FUND_ACCOUNT.getType());
        ResultVO<UserBo> resultVO = paymentMerchantApi.queryAccount(queryAccountVo, StringUtils.EMPTY);
        log.info("######平安账户######，queryAccountVo={}，resultVO={}", JSONUtils.toJSON(queryAccountVo), JSONUtils.toJSON(resultVO));
        if (resultVO == null || resultVO.isFail() || resultVO.getResult() == null) {
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }
        UserBo userBo = resultVO.getResult();
        String yuan = AmountCastUtil.castFen2Yuan(userBo.getWithdrawableBalance() == null ? 0L : userBo.getWithdrawableBalance());
        return AmountCastUtil.decimalFormat(yuan);
    }

    /**
     * 平安贷还款计划金额对账，验证平安贷还款计划是否与业务数据金额一致
     */
    @Override
    public void checkLoanAmount(boolean sendWarn) {
        List<PinganLoanCheckAmountDto> pinganLoanCheckAmountDtos = pinganLoanFinanceRefundPlanService.checkLoanAmount();
        log.info("平安贷还款计划金额对账，验证平安贷还款计划是否与业务数据金额一致：验证结果：{}", (CollectionUtils.isEmpty(pinganLoanCheckAmountDtos) ? "成功" : "失败"));
        if (CollectionUtils.isEmpty(pinganLoanCheckAmountDtos)) {
            return;
        }
        StringJoiner warnMsg = new StringJoiner("|");
        for (PinganLoanCheckAmountDto pinganLoanCheckAmountDto : pinganLoanCheckAmountDtos) {
            StringJoiner msg = new StringJoiner(",");
            msg.add("药帮忙订单号:" + pinganLoanCheckAmountDto.getBusinessPayNo());
            msg.add("支付订单号:" + pinganLoanCheckAmountDto.getPayNo());
            msg.add("支付时间:" + DateUtil.format(pinganLoanCheckAmountDto.getPayDay(), DateUtil.DATEFORMATSECOND));
            msg.add("借据号:" + pinganLoanCheckAmountDto.getLoanBalanceNo());
            msg.add("借据金额:" + pinganLoanCheckAmountDto.getLoanAmount());
            msg.add("订单金额:" + pinganLoanCheckAmountDto.getTradeAmount());
            warnMsg.add(msg.toString());
        }
        log.info("平安贷还款计划金额对账：{}", warnMsg.toString());
        if (sendWarn) {
            DingTalkMessage talkMessage = DingTalkMessage.builder()
                    .type("平安贷还款计划金额对账不一致，请及时处理")
                    .method("PaymentLoanServiceImpl.checkLoanAmount")
                    .msg("\r\n" + warnMsg.toString().replace("|", "\r\n"))
                    .param("")
                    .build();
            alarmService.alarm(talkMessage);
        }
    }

    @Override
    public ResultPageVo<List<PayFinanceRefundPlanVo>> queryByPage(PinganRepaymentPlanParamVO param) {
        ResultPageVo result = new ResultPageVo();
        try {
            PayFinanceRefundPlanQueryParam planQueryParam = new PayFinanceRefundPlanQueryParam();
            BeanUtils.copyProperties(param, planQueryParam);
            log.info("queryByPage param:{}", JSONObject.toJSONString(planQueryParam));
            //查询出总数
            Long total = pinganLoanFinanceRefundPlanService.getTotal(planQueryParam);
            log.info("queryByPage total:{}", total);
            if (null == total || 0 == total) {
                result = ResultPageVo.createSuccess();
            } else {
                List<PayFinanceRefundPlan> payFinanceRefundPlans = pinganLoanFinanceRefundPlanService.queryByPage(planQueryParam);
                log.info("queryByPage list:{}", JSONObject.toJSONString(payFinanceRefundPlans));
                List<PayFinanceRefundPlanVo> vos = po2Vo(payFinanceRefundPlans);
                result = ResultPageVo.createSuccess(param.getPage(), param.getPageSize(), total, vos);
            }
        } catch (Exception e) {
            log.error("queryByPage param:{}", param, e);
        }
        return result;
    }

    /**
     * 下载历史还款计划
     */
    @Override
    public String downloadLoanHuanKuanFileHistory(String startFileDate, boolean analy) {
        log.info("downloadLoanHuanKuanFileHistory:{}", startFileDate);
        if (StringUtils.isBlank(startFileDate)) {
            return "开始日期不允许为空！";
        }
        String lockKey = "downloadLoanHuanKuanFileHistory";
        if (!redisTemplate.opsForValue().setIfAbsent(lockKey, "default_value")) {
            return "有任务在处理中，请稍后重试";
        }
        redisTemplate.expire(lockKey, 30, TimeUnit.MINUTES);
        startFileDate = startFileDate.replace("-", "");
        final String finalStartFileDate = startFileDate;
        new Thread(() -> {
            String today = DateUtil.format(new Date(), DateUtil.DATEFORMATDAY_2);
            String currentDate = finalStartFileDate;
            while (currentDate.compareTo(today) <= 0) {
                log.info("开始处理:{}", currentDate);
                if (analy) {
                    log.info("下载文件并解析还款计划");
                    downloadLoanHuanKuanFile(currentDate, false);
                } else {
                    log.info("仅下载文件，不进行解析还款计划");
                    onlyDownloadLoanHuanKuanFile(currentDate);
                }
                log.info("结束处理:{}", currentDate);
                try {
                    currentDate = DateUtil.getDayOffset(DateUtil.DATEFORMATDAY_2, currentDate, 1);
                } catch (ParseException e) {
                    log.error("获取日期异常{}", e);
                }
                try {
                    Thread.sleep(5 * 1000L);
                } catch (InterruptedException e) {
                }
            }
            redisTemplate.delete(lockKey);
            log.info("处理结束............");
        }).start();
        return "任务已启动......";
    }

    private List<PayFinanceRefundPlanVo> po2Vo(List<PayFinanceRefundPlan> list) {
        List<PayFinanceRefundPlanVo> voList = new ArrayList<>();
        for (PayFinanceRefundPlan payFinanceRefundPlan : list) {
            PayFinanceRefundPlanVo vo = new PayFinanceRefundPlanVo();
            BeanUtils.copyProperties(payFinanceRefundPlan, vo);
            voList.add(vo);
        }
        return voList;
    }

    /**
     * 融资还款计划文件下载到本机，返回多个文件路径
     */
    @Override
    public void downloadLoanHuanKuanFile(String fileDate, boolean sendMsg) {
        log.info("PaymentLoanService#downloadHuanKuanFile start fileDate={}", fileDate);
        StringJoiner msg = new StringJoiner("\r\n");
        try {
            if (fileDate == null) {
                return;
            }
            if (!redisClient.setNx(fileDate, "1")) {
                log.info("PaymentLoanService#downloadHuanKuanFile setNx fail fileDate={}", fileDate);
                return;
            }
            String downloadFilePath = "/tmp/pingan_trade_check_bill_download/HuanKuan/" + fileDate + "/";
            String fileName = ApolloUtil.getProperty("pingan.appId") + "_PTSZD_HUANKUAN_" + fileDate + ".txt";
            log.info("PaymentLoanService#downloadHuanKuanFile fileName={}", fileName);
            // 1.组装参数
            FileRequest fileRequest = new FileRequest();
            fileRequest.setFileNo(fileName);
            PicUtil.createFilePath(downloadFilePath);
            fileRequest.setFilePathName(downloadFilePath + fileName);
            msg.add("账单文件：" + (downloadFilePath + fileName));

            long start = System.currentTimeMillis();
            // 2.发送请求下载文件
            apiClient.fileDownLoad(fileRequest);
            String realFilePath = downloadFilePath + fileName;
            log.info("PaymentLoanService#downloadLoanHuanKuanFile success realFilePath={}", realFilePath);
            msg.add("账单下载耗时：" + (System.currentTimeMillis() - start) + "ms");
            start = System.currentTimeMillis();

            // 3.解析文件
            final List<PayFinanceRefundPlan> data = getContent(realFilePath);
            msg.add("解析账单耗时：" + (System.currentTimeMillis() - start) + "ms，数据量：" + data.size() + "笔");
            start = System.currentTimeMillis();

            fillRepaymentInfo(data);
            msg.add("数据填充耗时：" + (System.currentTimeMillis() - start) + "ms");
            start = System.currentTimeMillis();

            //落库
            save(data, fileDate);
            msg.add("数据落库耗时：" + (System.currentTimeMillis() - start) + "ms");

            // 发送消息
            if (sendMsg) {
                DingTalkMessage talkMessage = DingTalkMessage.builder()
                        .type("平安还款计划账单解析")
                        .method("PaymentLoanServiceImpl.downloadLoanHuanKuanFile")
                        .msg("\r\n平安还款计划账单解析完成：\r\n" + msg.toString())
                        .param("\r\n" + JSON.toJSONString(fileRequest))
                        .build();
                alarmService.alarm(talkMessage);
            }
        } catch (Exception ex) {
            log.error("PaymentLoanService#downloadLoanHuanKuanFile error fileDate:{}", fileDate, ex);
            throw ex;
        } finally {
            redisClient.del(fileDate);
        }
    }

    @Override
    public void onlyDownloadLoanHuanKuanFile(String fileDate) {
        log.info("PaymentLoanService#downloadHuanKuanFile start fileDate={}", fileDate);
        try {
            if (StringUtils.isBlank(fileDate)) {
                return;
            }
            String downloadFilePath = "/tmp/pingan_trade_check_bill_download/HuanKuan/" + fileDate + "/";
            String fileName = ApolloUtil.getProperty("pingan.appId") + "_PTSZD_HUANKUAN_" + fileDate + ".txt";
            log.info("PaymentLoanService#downloadHuanKuanFile fileName={}", fileName);

            // 1.组装参数
            FileRequest fileRequest = new FileRequest();
            fileRequest.setFileNo(fileName);
            PicUtil.createFilePath(downloadFilePath);
            fileRequest.setFilePathName(downloadFilePath + fileName);

            // 2.发送请求下载文件
            apiClient.fileDownLoad(fileRequest);

            log.info("下载完成{}", fileName);
        } catch (Exception ex) {
            log.error("PaymentLoanService#downloadLoanHuanKuanFile error fileDate:{}", fileDate, ex);
        }
    }

    @Override
    public List<PayFinanceRefundPlan> getContent(String filePath) {
        InputStream in = null;
        BufferedReader br = null;
        List<PayFinanceRefundPlan> list = new ArrayList<>(32);
        final Date date = new Date();
        try {
            in = new FileInputStream(filePath);
            if (in != null) {
                br = new BufferedReader(new InputStreamReader(in, "GB18030"));
                String data;
                while ((data = br.readLine()) != null) {
                    final String[] strs = data.split("\\u0001");
//                    log.info("strs:length={},{}",strs.length,strs);
                    PayFinanceRefundPlan po = new PayFinanceRefundPlan();
                    po.setLoanBalanceNo(strs[0]);
                    po.setCurTerm(strs[1]);
                    po.setCurNum(strs[2]);
                    po.setCurRepayDate(DateUtil.parase(strs[3], DateUtil.DATEFORMATDAY_2));
                    po.setActRepayDate(DateUtil.parase(strs[4], DateUtil.DATEFORMATDAY_2));
                    final String payoffflag = strs[5];
                    if (StringUtils.isNotBlank(payoffflag)) {
                        po.setPayoffflag(Integer.parseInt(payoffflag));
                    } else {
                        po.setPayoffflag(0);
                    }
                    po.setCurRepayAmount(strs[6]);
                    po.setActRepayAmount(strs[7]);
                    po.setCurPrincipalAmount(strs[8]);
                    po.setActPrincipalAmount(strs[9]);
                    po.setCurInterestAmount(strs[10]);
                    po.setActInterestAmount(strs[11]);
                    po.setCurCompoundInterestAmount(strs[12]);
                    po.setActCompoundInterestAmount(strs[13]);
                    po.setCurSubsidyInterestAmount(strs[14]);
                    po.setActSubsidyInterestAmount(strs[15]);
                    po.setCreateTime(date);
                    po.setUpdateTime(date);
                    po.setRefund(0);
                    list.add(po);
                }
            }
        } catch (Exception ex) {
            log.error("downloadLoanHuanKuanFile getContent error:{}", filePath, ex);
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (Exception ex) {
                    log.error(ex.getMessage(), ex);
                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (Exception ex) {
                    log.error(ex.getMessage(), ex);
                }
            }
        }
        return list;
    }

    /**
     * 1、当天存在未结清的，删除表里所有记录 (借据号)
     * 2、当天存在已结清的
     * 2.1、删除表中所有未结清的
     * 2.2、删除表中与当前记录已结清数据一致的 (借据号、提前还款次数、实还日期、实还本金、结清状态)
     */
    private void save(List<PayFinanceRefundPlan> list, String fileDate) {
        long start = System.currentTimeMillis();

        // 1、当天存在未结清的，删除表里所有记录 (依据借据号)
        List<String> noPayOffList = list.stream().filter(x -> StringUtils.isNotEmpty(x.getLoanBalanceNo())).filter(x -> x.getPayoffflag() == 0).map(PayFinanceRefundPlan::getLoanBalanceNo).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(noPayOffList)) {
            final List<List<String>> partition = Lists.partition(noPayOffList, 500);
            for (List<String> delLoanBalanceNos : partition) {
                pinganLoanFinanceRefundPlanService.batchDelete(delLoanBalanceNos);
            }
            log.info("save batch 当天存在未结清的，删除表里所有记录 : {} ms，size {} ", System.currentTimeMillis() - start, noPayOffList.size());
        }
        start = System.currentTimeMillis();

        // 2、获取所有已结清数据，并进行删除：借据号、提前还款次数、实还日期、实还本金、结清状态
        List<PayFinanceRefundPlan> payOffData = list.stream().filter(x -> x.getPayoffflag() == 1).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(payOffData)) {
            for (PayFinanceRefundPlan payOffDatum : payOffData) {
                // 2.1、删除未结清数据
                PayFinanceRefundPlan deletePlanNoPayOff = new PayFinanceRefundPlan();
                deletePlanNoPayOff.setLoanBalanceNo(payOffDatum.getLoanBalanceNo());
                deletePlanNoPayOff.setPayoffflag(0);
                pinganLoanFinanceRefundPlanService.deleteLoan(deletePlanNoPayOff);

                // 2.2、删除已结清数据
                PayFinanceRefundPlan deletePlanPayOff = new PayFinanceRefundPlan();
                deletePlanPayOff.setLoanBalanceNo(payOffDatum.getLoanBalanceNo());
                deletePlanPayOff.setCurNum(payOffDatum.getCurNum());
                deletePlanPayOff.setActRepayDate(payOffDatum.getActRepayDate());
                deletePlanPayOff.setPayoffflag(payOffDatum.getPayoffflag());
                deletePlanPayOff.setActPrincipalAmount(payOffDatum.getActPrincipalAmount());
                pinganLoanFinanceRefundPlanService.deleteLoan(deletePlanPayOff);
            }
            log.info("save batch 获取所有已结清数据，并进行删除 : {} ms，size {} ", System.currentTimeMillis() - start, noPayOffList.size());
        }
        start = System.currentTimeMillis();

        // 3、保存记录
        if (CollectionUtils.isNotEmpty(list)) {
            final List<List<PayFinanceRefundPlan>> partition = Lists.partition(list, 500);
            for (List<PayFinanceRefundPlan> plans : partition) {
                pinganLoanFinanceRefundPlanService.batchInsert(plans);
            }
            log.info("save batch 保存记录 : {} ms，size {} ", System.currentTimeMillis() - start, noPayOffList.size());
        }

    }

    @Override
    public List<PayFinanceRefundPlan> export(PinganRepaymentPlanParamVO param) {
        //将前台参数转换成规范格式,并对参数做校验
        PayFinanceRefundPlanQueryParam queryParam = new PayFinanceRefundPlanQueryParam();
        BeanUtils.copyProperties(param, queryParam);
        final List<PayFinanceRefundPlan> payFinanceRefundPlans = pinganLoanFinanceRefundPlanService.pageListByParam(queryParam);
        for (PayFinanceRefundPlan payFinanceRefundPlan : payFinanceRefundPlans) {
            if (payFinanceRefundPlan.getPayoffflag() != null) {
                payFinanceRefundPlan.setPayoffflagStr(payFinanceRefundPlan.getPayoffflag() == 1 ? "已结清" : "未结清");
            } else {
                payFinanceRefundPlan.setPayoffflagStr("未知");
            }
            if (payFinanceRefundPlan.getRefund() == 1) {
                payFinanceRefundPlan.setRefundStr("有退款");
            } else {
                payFinanceRefundPlan.setRefundStr("无退款");
            }
        }
        return payFinanceRefundPlans;
    }

    @Override
    public void dealHistory(boolean db, boolean clearDb) {
        try {
            // 1、解析文件
            List<PayFinanceRefundPlan> data = new ArrayList<>();
            for (String line : PinganLoanService.HISTORY_EXCEL.split("\r\n")) {
                String[] cols = line.split(",");
                PayFinanceRefundPlan payFinanceRefundPlan = new PayFinanceRefundPlan();
                payFinanceRefundPlan.setLoanBalanceNo(cols[0]);
                payFinanceRefundPlan.setCurTerm(cols[1]);
                payFinanceRefundPlan.setCurNum(cols[2]);
                payFinanceRefundPlan.setCurRepayDate(DateUtil.parase(cols[3], DateUtil.DATEFORMATDAY_2));
                payFinanceRefundPlan.setActRepayDate(DateUtil.parase(cols[4], DateUtil.DATEFORMATDAY_2));
                payFinanceRefundPlan.setPayoffflag(Integer.valueOf(cols[5]));
                payFinanceRefundPlan.setCurRepayAmount(cols[6]);
                payFinanceRefundPlan.setActRepayAmount(cols[7]);
                payFinanceRefundPlan.setCurPrincipalAmount(cols[8]);
                payFinanceRefundPlan.setActPrincipalAmount(cols[9]);
                payFinanceRefundPlan.setCurInterestAmount(cols[10]);
                payFinanceRefundPlan.setActInterestAmount(cols[11]);
                payFinanceRefundPlan.setCurCompoundInterestAmount(cols[12]);
                payFinanceRefundPlan.setActCompoundInterestAmount(cols[13]);
                payFinanceRefundPlan.setCurSubsidyInterestAmount(cols[14]);
                payFinanceRefundPlan.setActSubsidyInterestAmount(cols[15]);
                payFinanceRefundPlan.setCreateTime(new Date());
                payFinanceRefundPlan.setUpdateTime(new Date());
                payFinanceRefundPlan.setRefund(0);
                data.add(payFinanceRefundPlan);
            }
            // 2、填充数据
            fillRepaymentInfo(data);
            for (PayFinanceRefundPlan refundPlan : data) {
                log.info("{}", JSON.toJSONString(refundPlan));
            }
            // 3、清库
            if (clearDb) {
                // 删除历史还款计划
                log.info("删除历史还款计划......");
                pinganLoanFinanceRefundPlanService.deleteLoan(new PayFinanceRefundPlan());
                log.info("删除历史还款计划完成......");
            }
            // 4、落库
            if (db) {
                // 重新解析
                log.info("执行落库操作......");
                pinganLoanFinanceRefundPlanService.batchInsert(data);
            }
        } catch (Exception e) {
            log.error("error:", e);
        }
    }

    private void fillRepaymentInfo(List<PayFinanceRefundPlan> list) {
        final List<List<PayFinanceRefundPlan>> partition = Lists.partition(list, 200);
        for (List<PayFinanceRefundPlan> data : partition) {
            List<String> LoanBalanceNoList = data.stream().map(PayFinanceRefundPlan::getLoanBalanceNo).collect(Collectors.toList());
            List<PayResultOrder> payResultOrders = payResultOrderService.queryByChannelTransactionNo(LoanBalanceNoList);
            log.info("fillRepaymentInfo payResultOrders size:{}", payResultOrders.size());
            if (CollectionUtils.isEmpty(payResultOrders)) {
                continue;
            }

            List<String> payNoList = payResultOrders.stream().map(PayResultOrder::getPayNo).collect(Collectors.toList());
            List<PayFinanceRefundPlan> planTmp = ecOrderRemote.queryListByPayNo(payNoList);
            if (CollectionUtils.isEmpty(planTmp)) {
                continue;
            }
            final Map<String, PayResultOrder> payResultMap = payResultOrders.stream().collect(Collectors.toMap(PayResultOrder::getChannelTransactionNo, Function.identity(), (x1, x2) -> x1));

            final Map<String, PayFinanceRefundPlan> collect = planTmp.stream().collect(Collectors.toMap(PayFinanceRefundPlan::getPayNo, Function.identity(), (x1, x2) -> x1));
            for (PayFinanceRefundPlan payFinanceRefundPlan : data) {
                final PayResultOrder payResultOrder = payResultMap.get(payFinanceRefundPlan.getLoanBalanceNo());
                if (payResultOrder != null) {
                    payFinanceRefundPlan.setReceiverId(payResultOrder.getReceiveId());
                    payFinanceRefundPlan.setPayNo(payResultOrder.getPayNo());
                    payFinanceRefundPlan.setPayDay(payResultOrder.getCtime());
                    payFinanceRefundPlan.setChannelPayNo(payResultOrder.getChannelChargeNo());
                    payFinanceRefundPlan.setPayerId(payResultOrder.getPayer()); // 付款人在支付平台侧ID
                    payFinanceRefundPlan.setTradeAmount((BigDecimal.valueOf(payResultOrder.getAmount()).multiply(new BigDecimal("0.01"))));

                    PayFinanceRefundPlan refundPlanTmp = collect.get(payResultOrder.getPayNo());
                    if (refundPlanTmp != null) {
                        payFinanceRefundPlan.setThirdApplyNo(refundPlanTmp.getThirdApplyNo());
                        payFinanceRefundPlan.setBusinessPayerId(refundPlanTmp.getBusinessPayerId());
                        payFinanceRefundPlan.setPayerName(refundPlanTmp.getPayerName());
                        payFinanceRefundPlan.setBusinessReceiverId(refundPlanTmp.getBusinessReceiverId());
                        payFinanceRefundPlan.setReceiverName(refundPlanTmp.getReceiverName());
                        payFinanceRefundPlan.setBusinessPayNo(refundPlanTmp.getBusinessPayNo());
                    }
                }
                // 若已结清，则判断是否为退款记录：通过payNo、实际还款日期判断
                if (payFinanceRefundPlan.getPayoffflag().equals(1)) {
                    List<RefundOrder> refundOrders = refundOrderMapper.selectListByPayNo(payFinanceRefundPlan.getPayNo());
                    if (CollectionUtils.isEmpty(refundOrders)) {
                        payFinanceRefundPlan.setRefund(0);
                    } else {
                        for (RefundOrder refundOrder : refundOrders) {
                            if (refundOrder.getStatus().intValue() != 3) {
                                continue;
                            }
                            if (DateUtil.format(refundOrder.getSuccessTime(), DateUtil.DATEFORMATDAY).equals(DateUtil.format(payFinanceRefundPlan.getActRepayDate(), DateUtil.DATEFORMATDAY))) {
                                payFinanceRefundPlan.setRefund(1);
                                break;
                            }
                        }
                    }
                }
            }
        }

    }

}
